/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */
using System;
using System.Collections;
using System.IO;
using System.Xml;
using ELE.Common.Util;

namespace ELE.EnterpriseLibrary.Naming.Configuration
{
  /// <summary>
  /// Summary description for ContextConfigurationParser.
  /// </summary>
  public class ContextConfigurationParser : GenericConfigurationParser
  {
    /// <summary>
    /// Parses a context configuration file into ContextConfiguration object
    /// 
    /// </summary>
    /// <param name="fileName"></param>
    /// <returns></returns>
    public static ContextConfiguration ParseContextConfiguration(string fileName)
    {
      if(fileName == null)
      {
        //TODO:Throw an exception here
        return null;
      }
      ContextConfiguration conf = new ContextConfiguration();
      
      XmlDocument doc = null;
      try
      {
        fileName = FileUtil.FullyQualify( StringUtils.ForwardToBackSlash(fileName));
      }
      catch(Exception e)
      {
        throw e;
      }
      doc = XML.Parse(fileName);
      if (doc != null)
      {
        conf.Environment = ParseContextEnvironment(doc);
        string rootDir = ParseContextRootDirectory(doc);
        conf.LoggerConfigurationFile = ParseContextLogger(doc);
        if (string.IsNullOrEmpty( rootDir))
        {
          rootDir = FileUtil.TrimFilename(FileUtil.VerifySeparator( fileName));
        }
        if(rootDir.EndsWith(StringUtils.SLASH_FORWARD))
        {
          rootDir = rootDir.Substring(0, (rootDir.Length - 1));
        }
        conf.GlobalEntries = ParseGlobalEntries(doc);
        conf.SubContexts = ParseSubContexts(doc);
        conf.RootDirectory = rootDir;
      }
      return conf;
    }
    /// <summary>
    /// Gets a list of global entries that can be used by any component in the context.
    /// </summary>
    /// <param name="pDoc"></param>
    /// <returns></returns>
    public static Hashtable ParseGlobalEntries(XmlDocument pDoc)
    {
      if(pDoc == null)
      {
        return null;
      }
      Hashtable globals = new Hashtable();
      XmlNode geNode = SelectNodeByNameIgnoreCase((XmlNode)pDoc.DocumentElement, ContextConfiguration.TAGNAME_GLOBALENTRIES);
      if(geNode != null)
      {
        XmlNodeList geNodes = geNode.ChildNodes;

        if(geNodes != null)
        {
          foreach(XmlNode node in geNodes)
          {
            if (node.Name == ContextConfiguration.TAGNAME_ADD)
            {
              string entryName = node.Attributes[ContextConfiguration.ATTRIBUTENAME_NAME].Value;
              object entryValue = GetNodeValue(node);
              globals.Add(entryName, entryValue);
            }
          }
        }
      }
      return globals;
    }
    /// <summary>
    /// Gets a list of global entries that can be used by any component in the context.
    /// </summary>
    /// <param name="pDoc"></param>
    /// <returns></returns>
    public static Hashtable ParseSubContexts(XmlDocument pDoc)
    {
      if (pDoc == null)
      {
        return null;
      }
      Hashtable subCtxs = new Hashtable();
      XmlNode scNode = SelectNodeByNameIgnoreCase((XmlNode)pDoc.DocumentElement, ContextConfiguration.TAGNAME_SUBCONTEXT);
      if (scNode != null)
      {
        XmlNodeList scNodes = scNode.ChildNodes;

        if (scNodes != null)
        {
          foreach (XmlNode node in scNodes)
          {
            if (node.Name == ContextConfiguration.TAGNAME_ADD)
            {
              object subContext = null;
              XmlNode configNode = SelectNodeByNameIgnoreCase(node, ContextConfiguration.TAGNAME_CONFIGFILE);
              //Must define a configuration file location or can't create the context
              if (configNode != null)
              {
                subContext = configNode.InnerText;
              }
              else
              {
                XmlNode refNode = SelectNodeByNameIgnoreCase(node, ContextConfiguration.TAGNAME_REFERENCE);
                if (refNode != null)
                {
                  ComponentInitializer cInit = new ComponentInitializer();
                  cInit.Type = ComponentInitializer.TYPE_REF;
                  cInit.Value = refNode.InnerText;
                  subContext = cInit;
                }

              }
              if (subContext != null)
              {
                subCtxs.Add(node.Attributes[ContextConfiguration.ATTRIBUTENAME_NAME].Value, subContext);
              }
            }
          }
        }
      }
      return subCtxs;
    }
    /// <summary>
    /// Get the location of the root of this context
    /// </summary>
    /// <param name="pDoc"></param>
    public static string ParseContextRootDirectory(XmlDocument pDoc)
    {
      if(pDoc == null)
      {
        return null;
      }
      IList rootDirNodes = SelectNodesByNameIgnoreCase(pDoc.DocumentElement, ContextConfiguration.TAGNAME_ROOT);
      if(rootDirNodes != null && rootDirNodes.Count > 0)
      {
        return ((XmlNode)rootDirNodes[0]).InnerText;
      }
      return null;
    }
    /// <summary>
    /// Get the location of the logger's configuration file of this context
    /// </summary>
    /// <param name="pDoc"></param>
    public static string ParseContextLogger(XmlDocument pDoc)
    {
      if (pDoc == null)
      {
        return null;
      }
      IList rootDirNodes = SelectNodesByNameIgnoreCase(pDoc.DocumentElement, ContextConfiguration.TAGNAME_LOGCONFIG);
      if (rootDirNodes != null && rootDirNodes.Count > 0)
      {
        return ((XmlNode)rootDirNodes[0]).InnerText;
      }
      return null;
    }
    /// <summary>
    /// Get the environment settings of the context
    /// </summary>
    /// <param name="pDoc"></param>
    /// <returns></returns>
    public static Hashtable ParseContextEnvironment(XmlDocument pDoc)
    {
      if(pDoc == null)
      {
        //throw an exception
        return null;
      }
      IList nodes = SelectNodesByNameIgnoreCase(pDoc.DocumentElement, ContextConfiguration.TAGNAME_ENVIRONMENT);
      Hashtable results = new Hashtable();
      if(nodes != null && nodes.Count > 0)
      {
        foreach(XmlNode node in nodes)
        {
          Hashtable envValues = ProcessEnvironmentNode(node);
          if(envValues != null && envValues.Count > 0)
          {
            foreach(string name in envValues.Keys)
            {
              results.Add(name, envValues[name]);
            }
            //break on the first environment tag that is populated we only need one
            break;
          }
        }
      }
      return results;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="pNode"></param>
    /// <returns></returns>
    protected static Hashtable ProcessEnvironmentNode(XmlNode pNode)
    {
      if(pNode == null || !pNode.HasChildNodes)
      {
        return null;
      }
      Hashtable environment = new Hashtable();
      //Get initialcontext factories
      if (pNode.ChildNodes != null)
      {
        foreach (XmlNode node in pNode.ChildNodes)
        {
          if (node.Name == GenericConfiguration.TAGNAME_ADD)
          {
            string envNodeName = node.Attributes[GenericConfiguration.ATTRIBUTENAME_NAME].Value;
            object envNodeValue = GetNodeValue(node);
            environment.Add(envNodeName, envNodeValue);
          }
        }
      }
      return environment;
    }
  }
}
